﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

namespace MVCToolkit.Validations
{
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
    public class RequiredIfAttribute : ValidationAttribute, IClientValidatable
    {
        private const string DefaultErrorMessageFormatString = "The {0} field is required.";

        private readonly string _dependentPropertyName;
        private readonly Comparison _dependentPropertyComparison;
        private readonly object _dependentPropertyValue;

        public RequiredIfAttribute(string dependentPropertyName,
                                   Comparison dependentPropertyComparison,
                                   object dependentPropertyValue)
        {
            _dependentPropertyName = dependentPropertyName;
            _dependentPropertyComparison = dependentPropertyComparison;
            _dependentPropertyValue = dependentPropertyValue;
            ErrorMessage = DefaultErrorMessageFormatString;
        }
        public override string FormatErrorMessage(string name)
        {
            return string.Format(ErrorMessageString, name, _dependentPropertyName);
        }

        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata,
                                 ControllerContext context)
        {
            var rule = new ModelClientValidationRule();
            rule.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName());
            rule.ValidationParameters.Add("other", _dependentPropertyName);
            rule.ValidationParameters.Add("comp", _dependentPropertyComparison.ToString().ToLower());
            rule.ValidationParameters.Add("value", _dependentPropertyValue);
            rule.ValidationType = "requiredif";
            yield return rule;
        }

        private bool ValidateDependentProperty(object actualPropertyValue)
        {
            switch (_dependentPropertyComparison)
            {
                case Comparison.IsNotEqualTo:
                    return actualPropertyValue == null ? _dependentPropertyValue != null : !actualPropertyValue.Equals(_dependentPropertyValue);
                default:
                    return actualPropertyValue == null ? _dependentPropertyValue == null : actualPropertyValue.Equals(_dependentPropertyValue);
            }
        }

        protected override System.ComponentModel.DataAnnotations.ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            if (value == null)
            {
                var property = validationContext.ObjectInstance.GetType().GetProperty(_dependentPropertyName);
                var dependentPropertyValue = property.GetValue(validationContext.ObjectInstance, null);

                if (ValidateDependentProperty(dependentPropertyValue))
                {
                    return new System.ComponentModel.DataAnnotations.ValidationResult(string.Format(ErrorMessageString, validationContext.DisplayName));
                }
            }

            return System.ComponentModel.DataAnnotations.ValidationResult.Success;
        }
    }

    public enum Comparison
    {
        IsEqualTo,
        IsNotEqualTo
    }
}
